package com.lsl.rdf.advice;

import com.lsl.rdf.callback.LogAdviceCallback;
import com.lsl.rdf.utils.RequestInfoUtil;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicLong;

/**
 * Created by lsl on 2021/6/21.
 */
public class LogAdvice implements MethodInterceptor {

    private static final Logger log = LoggerFactory.getLogger(LogAdvice.class);
    private static final AtomicLong LOG_ID_COUNTER = new AtomicLong(1);

    /**
     * 打印日志模板
     */
    private String printLogPattern;

    /**
     * 应用名称
     */
    private String applicationName;

    /**
     * 应用端口号
     */
    private String applicationPort;

    /**
     * 回调接口
     */
    private LogAdviceCallback logAdviceCallback;

    public LogAdvice(String printLogPattern, String applicationName, String applicationPort, LogAdviceCallback logAdviceCallback) {
        this.printLogPattern = printLogPattern;
        this.applicationName = applicationName;
        this.applicationPort = applicationPort;
        this.logAdviceCallback = logAdviceCallback;
    }

    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        if (Objects.nonNull(logAdviceCallback)) {
            logAdviceCallback.syncBeforeAdvice(methodInvocation);
        }

        long startTime = System.currentTimeMillis();
        Object result = methodInvocation.proceed();
        long endTime = System.currentTimeMillis();

        printLog(methodInvocation, (endTime - startTime), result);

        if (Objects.nonNull(logAdviceCallback)) {
            logAdviceCallback.syncAfterAdvice(methodInvocation, result);
        }
        return result;
    }

    /**
     * 打印日志
     *
     * @param methodInvocation 调用方法
     * @param respTime         响应耗时
     */
    private void printLog(MethodInvocation methodInvocation, long respTime, Object result) throws UnknownHostException {
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        String requestIp = "";
        String requestUrl = "";
        String method = "";
        if (Objects.nonNull(requestAttributes)) {
            HttpServletRequest request = requestAttributes.getRequest();
            requestIp = RequestInfoUtil.getRequestIp(request);
            requestUrl = RequestInfoUtil.getRequestURL(request);
            method = request.getMethod();
        }

        if (StringUtils.isNotBlank(printLogPattern)) {
            String printLogString = printLogPattern
                    .replaceAll("\\{tid}", String.valueOf(LOG_ID_COUNTER.getAndAdd(1L)))
                    .replaceAll("\\{app}", applicationName)
                    .replaceAll("\\{addr}", RequestInfoUtil.getHostAddress())
                    .replaceAll("\\{port}", applicationPort)
                    .replaceAll("\\{method}", method)
                    .replaceAll("\\{reqIp}", requestIp)
                    .replaceAll("\\{reqUrl}", requestUrl)
                    .replaceAll("\\{reqMethod}", getMethod(methodInvocation))
                    .replaceAll("\\{reqArgs}", Arrays.toString(methodInvocation.getArguments()))
                    .replaceAll("\\{respArgs}", Objects.nonNull(result) ? result.toString() : "[]")
                    .replaceAll("\\{respTime}", String.valueOf(respTime));
            log.info(printLogString);
        }
    }

    /**
     * 获取方法全路径名
     */
    private String getMethod(MethodInvocation method) {
        if (Objects.isNull(method)) {
            return "";
        }
        String methodName = method.getMethod().getName();
        String className = method.getMethod().getDeclaringClass().getName();
        return className + "." + methodName;
    }
}
